#ifndef __tenacitas_log__logger__h__
#define __tenacitas_log__logger__h__


// =============================================================================
/** @file
 In this file is defined the @p tenacitas::log::logger class
*/

// =============================================================================
/** todo

*/


// =============================================================================
//                                 C++ Headers
#include <string>
#include <cstdint>
#include <mutex>
#include <map>
#include <utility>
#include <sstream>
#include <memory>
#include <functional>

// =============================================================================
//                                 3rds Headers
#include <boost/thread/thread.hpp>

// =============================================================================
//                                 Our Headers

/** In the <tt>level.h</tt> file we have the definition of the log levels  */
#include "level.h"
#include "exception.h"

// =============================================================================
//                                 Namespaces
using namespace std;


// =============================================================================
//                                 Macro Contants
/** */


// =============================================================================
//                                 Macro Commands

/** This macro allows a message to be logged in @p tenacitas::log::level::trace level

 @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
#define log_trace( logger ) logger.new_line( tenacitas::log::level::trace, __FILE__, __func__, __LINE__ )

/** This macro allows a message to be logged in @p tenacitas::log::level::debug level 

 @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
#define log_debug( logger ) logger.new_line( tenacitas::log::level::debug, __FILE__, __func__, __LINE__ )

/** This macro allows a message to be logged in @p tenacitas::log::level::info level 

 @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
#define log_info( logger ) logger.new_line( tenacitas::log::level::info, __FILE__, __func__, __LINE__ )

/** This macro allows a message to be logged in @p tenacitas::log::level::warn level 

 @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
#define log_warn( logger ) logger.new_line( tenacitas::log::level::warn, __FILE__, __func__, __LINE__ )

/** This macro allows a message to be logged in @p tenacitas::log::level::error level 

 @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
#define log_error( logger ) logger.new_line( tenacitas::log::level::error, __FILE__, __func__, __LINE__ )

/** This macro allows a message to be logged in @p tenacitas::log::level::critical level 

 @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
#define log_critical( logger ) logger.new_line( tenacitas::log::level::critical, __FILE__, __func__, __LINE__ )


/** namespace of the organization */
namespace tenacitas {

    /** namespace of the project */
    namespace log {

        // =========================== Typedefs ===========================

        // ============================ Pre-Declarations ==================

        // ============================ Attributes ========================
        /** */

        // ============================ Functions =========================
        
        // ============================ Classes ===========================

        /** Base class that provides logging facilities

        @tparam t_media offers an abstraction about the media where the
        log messages will be printed, and it must implement:

        <tt>typedef ptr<tt>, which defines a pointer to
        a @p t_media object

        <tt>template <typename t_data> void write ( const t_data & )<tt>,
        which writes any type of data to the media

        <tt>void new_line( level p_level, const char * p_file,
        const char * p_function, int64_t p_line )</tt>, which starts a new
        log line in the media

        <tt>static t_media::ptr create( boost::thread::id )</tt>, which
        creates a pointer to a @p t_media object

        You can see an example of @p t_media implementation in
        tenacitas::log::file

        <b>A word to the programmer user</b>
        You, dear programmer, should avoid using the
        tenacitas::log::logger::new_line and <tt> operator<< <tt> members, and
        use the macros commands @p log_trace, @p log_debug, @p log_info,
        @p log_warn, @p log_error or @p log_critical instead.

        You should use the static method tenacitas::log::logger::set_level_all
        to define the tenacitas::log::level to be used by all the @p logger
        objects.

        You can change the tenacitas::log::level for all the @p logger objects
        to be created (not the ones already created), by calling
        tenacitas::log::logger::set_level_all again.

        When creating a @p logger, you can define a tenacitas::log::level to be
        used by only for it.

        You would better call tenacitas::log::logger::can_log before actualy
        logging a message to improve performance.

        @nosubgrouping
        */
        template <typename t_media>
        class logger {

        public:

            //    logger - Public Constructors

            /** Nothing to be done in the destructor */
            ~logger( ) { }

            /** Constructor

            @param [in] p_level is the tenacitas::log::level to be used
            by this @p logger object. Its default value is the log level
            defined for all the @p logger objects, defined using the
            @p set_level_all below, and retrieved via @p get_level_all

            @note Please, read the documentation for tenacitas::log::level
            to understand how log levels work in tenacitas::log

             @attention A @p logger object can not be passed to a function that
             will run in another thread. This is due to the concept that the
             tenacitas::log classes work to create log for a specific thread. So,
             if a @p logger object logs messages in a thread different from the
             one it was created, it will mix log messages of both threads.
            */
            logger( level p_level = logger::get_level_all( ) )
                : // saves the thread id for the current thread
                  m_thread_id( boost::this_thread::get_id( ) ),
                  // retrieves (or creates) a @p media object
                  m_media( m_threads_loggers.get_media( m_thread_id ) ),
                  // saves the log level defined
                  m_level( p_level ) { }
            

            /** Copies are not-allowed */
            logger ( const logger & p_logger ) = delete;

            /** Moves are not-allowed */
            logger ( logger && p_logger ) = delete;

            //    logger - Public Accessors

            /** Sets log level for all the @p logger objects

            It only affects logger objects created after this method is called

            @param [in] p_level is the tenacitas::log::level to be used
            by all @p logger objects created after this method is called */
            static void set_level_all( const level & p_level_all ) {
                m_level_all = p_level_all;
            }

            /** @return the log level for all the @p logger objects */
            static level get_level_all( ) {
                return m_level_all;
            }
            

            /** Resets the log level defined to this @p logger object

            @param [in] p_level is the new tenacitas::log::level to be used
            by this @p logger object. */
            void set_level( const level & p_level ) {
                m_level = p_level;
            }

            /** @return the log level for this @p logger object */
            const level & get_level( ) const {
                return m_level;
            }

            /** Informs if a message log can be logged

            Before logging anything, one should, for performance sake, verify
            if the message will actualy be logged.
            <b>"Why?"</b>, you may ask
            Well, suppose a log such as:
            <tt> log_debug( logger ) << "very long string..."
                                     << a_double
                                     << "another long string";
            </tt>
            If the log level for the @p logger object is
            tenacitas::log::level::error, nothing will be logged. So, the work
            done to parse the parts of the log message will have been for
            nothing! Therefore, one should write:
            <tt>
            if ( logger.can_log(tenacitas::log::level::debug ) {
                log_debug( logger ) << "very long string..."
                                    << a_double
                                    << "another long string";
            }

            @param [in] p_level is the level one wants to check if a log
            will be logged

            @attention <b>tenacitas::log::level::error and
            tenacitas::log::level::critical will allways be logged, no matter
            the current log level. So, it is not necessary to check for the
            log level if one wants to use @p log_error or @p log_critical</b>
            */
            bool can_log( const level & p_level ) const {
                if ( ( p_level == level::error ) ||
                     ( p_level == level::critical ) ) {
                    return true;
                }
                return ( p_level >= m_level );
            }

            //    logger - Public Processors

            /** Starts a new log line

             @attention: One should not call  this method directly, but use the
             @p log_trace, @p log_debug, @p log_info, @p log_warn,
             @p logic_error or @p log_critical

             This methods inform the @p t_media class that a new line is
             starting. Depending on the concrete type of @p t_media, this can
             mean a new line in a file, a new record in a database table, or
             anything else.

             @param [in] p_level is the tenacitas::log::level of the new line
             @param [in] p_file is the source file where the log is occurring
             @param [in] p_function is the function (method) that is log is
             occurring
             @param [in] p_line is the line it the @p p_file where the log is
             occurring

             @attention A @p logger object can not be passed to a function that
             will run in another thread. This is due to the concept that the
             tenacitas::log classes work to create log for a specific thread. So,
             if a @p logger object logs messages in a thread different from the
             one it was created, it will mix log messages for both threads.

             @throw std::string if this @p logger is being used in a
             different thread from where it was created
            */
            logger & new_line( level p_level, const char * p_file,
                               const char * p_function, int64_t p_line ) {

                // check if it is still in the same thread as it was created
                if( m_thread_id != boost::this_thread::get_id( ) ) {

                    // NO!

                    // to format the exception message
                    std::stringstream l_stream;

                    // the original thread
                    int64_t l_this_thread_id = th2int( m_thread_id );

                    // this 'other' thread
                    int64_t l_new_thread_id =
                            th2int( boost::this_thread::get_id( ) );
                    
                    l_stream << "This log is to be used with thread "
                             << std::dec << l_this_thread_id
                             << ", but its being used with thread "
                             << std::dec << l_new_thread_id ;
                    tenacitas::log::exception l_ex(
                        make_log_exception( l_stream.str( ) ) );
                    throw ( l_ex );
                }
                
                // the current thread is the same as the one where this logger
                // object was created

                // but, the requested log level is allowed now?
                if ( can_log( p_level ) ) {
                    // ok! so we ask the @p t_media object to actually
                    // create a new log line
                    m_media->new_line( p_level, p_file, p_function, p_line );
                }
                return *this;
            }

            /** Outputs any kind of data

            @tparam t_data is the type of the data to be logged

            @param p_data is the data to be logged
            */
            template <typename t_data>
            logger & operator << ( const t_data & p_data ) {
                m_media->write( p_data );
                return *this;
            }

            /** Outputs any kind of data

            @tparam t_data is the type of the data to be logged

            @param p_data is the data to be logged
            */
            template <typename t_data>
            logger & operator << ( t_data && p_data ) {
                m_media->write( p_data );
                return *this;
            }

        private:

            //    logger - Private Internal Types

            /** Function object to be called when a thread ends */
            struct at_thread_end {

                //    at_thread_end - Public Operators

                /** */
                void operator( )( ) {
                    m_threads_loggers.delete_logger( boost::this_thread::get_id( ) );
                }
            };

            /** Type for the @p t_media parameter */
            typedef t_media media;

            /** Type for the pointer to a @p t_media object */
            typedef typename media::ptr media_ptr;

            /** Type for a pointer to the logger class */
            typedef std::shared_ptr<logger> ptr;

            // /** This is collection of <tt>t_media::ptr </tt> objects and the
            //  thread to which they are associated
            // */
            // typedef std::map<boost::thread::id, media_ptr> threads_loggers;

            // /** The type of iterator of the collection above */
            // typedef typename threads_loggers::iterator loggers_iterator;

            class threads_loggers {
            public:

                ~threads_loggers( ) {
                    // std::cout << "~threads_loggers" << std::endl;
                    
                    iterator l_end = m_map.end( );
                    for ( iterator l_ite = m_map.begin( );
                          l_ite != l_end;
                          ++l_ite ) {

                        // std::cout << "about to lock" << std::endl;
                        std::lock_guard<std::mutex> lock( m_mutex );
                        // std::cout << "lock released" << std::endl;

                        l_ite->second->on_thread_end( );

                        // iterator l_aux = l_ite;
                        // ++l_aux;
                            
                        m_map.erase( l_ite );

                        // l_ite = l_aux;
                    }
                }

                /** Retrieves a @p media_ptr associated to a thread

                    This method will search for a @p media_ptr in the collection of
                    @p media_ptr, and it will return it; or it will create a
                    @p media_ptr, associate it to the thread, and insert it into
                    the collection

                    @param [in] p_thread_id is the thread one wants to retrieve the
                    @p media_ptr associated

                */
                media_ptr get_media( boost::thread::id p_thread_id ) {

                    // already created?
                    iterator l_ite = m_map.find( p_thread_id );

                    if ( l_ite != m_map.end( ) ) {
                        // yes!
                        return ( l_ite->second );
                    }

                    // inserting the logger into the collection of loggers,
                    // associating to theirs threads

                    media_ptr l_media = media::create( p_thread_id );

                    std::pair<boost::thread::id, media_ptr> l_pair( p_thread_id,
                                                                    l_media );

                    std::lock_guard<std::mutex> lock(m_mutex);

                    // I can lock only here, because the thread l_th_id can not be
                    // modified in another thread
                    boost::this_thread::at_thread_exit( at_thread_end( ) );

                    // returning a reference to the just inserted logger
                    return (  m_map.insert( l_pair ).first->second );
                }

                /** Deletes a <tt>(media_ptr, boost::thread::id) from the collection

                    @param [in] p_thread_id is the thread to be removed, together to
                    its associated @p media_ptr from the collection
                */
                void delete_logger( boost::thread::id p_thread_id ) {

                    iterator l_ite = m_map.find( p_thread_id );

                    if ( l_ite != m_map.end( ) ) {

                        {
                            std::cout << "delete_logger for "
                                      << th2int( p_thread_id ) << std::endl;
                            // I can lock only here, because exclusion of p_thread_id can
                            // not happen in another thread
                            std::lock_guard<std::mutex> lock( m_mutex );

                            l_ite->second->on_thread_end( );

                            m_map.erase( l_ite );
                        }
                    }
                }

            private:
                typedef std::map<boost::thread::id, media_ptr> map;

                /** The type of iterator of the collection above */
                typedef typename map::iterator iterator;

                map m_map;
                
            };


            //    logger - Private Processors
            
            // /** Retrieves a @p media_ptr associated to a thread

            //   This method will search for a @p media_ptr in the collection of
            //   @p media_ptr, and it will return it; or it will create a
            //   @p media_ptr, associate it to the thread, and insert it into
            //   the collection

            //   @param [in] p_thread_id is the thread one wants to retrieve the
            //   @p media_ptr associated

            // */
            // static media_ptr get_media( boost::thread::id p_thread_id ) {

            //     // already created?
            //     loggers_iterator l_ite = m_threads_loggers.find( p_thread_id );

            //     if ( l_ite != m_threads_loggers.end( ) ) {
            //         // yes!
            //         return ( l_ite->second );
            //     }

            //     // inserting the logger into the collection of loggers,
            //     // associating to theirs threads

            //     media_ptr l_media = media::create( p_thread_id );

            //     std::pair<boost::thread::id, media_ptr> l_pair( p_thread_id,
            //                                                     l_media );

            //     std::lock_guard<std::mutex> lock(m_mutex);

            //     // I can lock only here, because the thread l_th_id can not be
            //     // modified in another thread
            //     boost::this_thread::at_thread_exit( at_thread_end( ) );

            //     // returning a reference to the just inserted logger
            //     return (  m_threads_loggers.insert( l_pair ).first->second );
            // }

            // /** Deletes a <tt>(media_ptr, boost::thread::id) from the collection

            //   @param [in] p_thread_id is the thread to be removed, together to
            //   its associated @p media_ptr from the collection
            // */
            // static void delete_logger( boost::thread::id p_thread_id ) {

            //     loggers_iterator l_ite = m_threads_loggers.find( p_thread_id );

            //     if ( l_ite != m_threads_loggers.end( ) ) {

            //         {
            //             // I can lock only here, because exclusion of p_thread_id can
            //             // not happen in another thread
            //             std::lock_guard<std::mutex> lock( m_mutex );

            //             l_ite->second->on_thread_end( );

            //             m_threads_loggers.erase( l_ite );
            //         }
            //     }
            // }


            //    logger - Private Attributes            

            /** Thread where this @p logger was created */
            boost::thread::id m_thread_id;

            /** Class that actually logs the message */
            media_ptr m_media;

            /** Log level of this @p logger object */
            level m_level;

            /** The collection of boost::thread::id and the @p media_ptr
            associated to it */
            static threads_loggers m_threads_loggers;

            /** To protect access to @p m_threads_loggers */
            static std::mutex m_mutex;

            /** Log level commom to all the @p logger objects */
            static level m_level_all;

        };
    }
}

using namespace tenacitas::log;

/** Default construction for the collection */
template <typename t_media>
typename logger<t_media>::threads_loggers
logger<t_media>::
m_threads_loggers;

/** Default construction for the mutex */
template <typename t_media>
std::mutex
logger<t_media>::
m_mutex;

/** Default log level for all the @p logger objects is
 tenacitas::log::level::info */
template <typename t_media>
level
logger<t_media>::
m_level_all( level::info );


#endif





// #ifndef __tenacitas_log__logger__h__
// #define __tenacitas_log__logger__h__


// // =============================================================================
// /** @file
//  In this file is defined the @p tenacitas::log::logger class
// */

// // =============================================================================
// /** todo

// */


// // =============================================================================
// //                                 C++ Headers
// #include <string>
// #include <cstdint>
// #include <mutex>
// #include <map>
// #include <utility>
// #include <sstream>
// #include <memory>
// #include <functional>

// // =============================================================================
// //                                 3rds Headers
// #include <boost/thread/thread.hpp>

// // =============================================================================
// //                                 Our Headers

// /** In the <tt>level.h</tt> file we have the definition of the log levels  */
// #include "level.h"
// #include "exception.h"

// // =============================================================================
// //                                 Namespaces
// using namespace std;


// // =============================================================================
// //                                 Macro Contants
// /** */


// // =============================================================================
// //                                 Macro Commands

// /** This macro allows a message to be logged in @p tenacitas::log::level::trace level

//  @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
// #define log_trace( logger ) logger.new_line( tenacitas::log::level::trace, __FILE__, __func__, __LINE__ )

// /** This macro allows a message to be logged in @p tenacitas::log::level::debug level 

//  @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
// #define log_debug( logger ) logger.new_line( tenacitas::log::level::debug, __FILE__, __func__, __LINE__ )

// /** This macro allows a message to be logged in @p tenacitas::log::level::info level 

//  @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
// #define log_info( logger ) logger.new_line( tenacitas::log::level::info, __FILE__, __func__, __LINE__ )

// /** This macro allows a message to be logged in @p tenacitas::log::level::warn level 

//  @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
// #define log_warn( logger ) logger.new_line( tenacitas::log::level::warn, __FILE__, __func__, __LINE__ )

// /** This macro allows a message to be logged in @p tenacitas::log::level::error level 

//  @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
// #define log_error( logger ) logger.new_line( tenacitas::log::level::error, __FILE__, __func__, __LINE__ )

// /** This macro allows a message to be logged in @p tenacitas::log::level::critical level 

//  @param [in] logger is an instance of some specialization of @p tenacitas::log::logger class */
// #define log_critical( logger ) logger.new_line( tenacitas::log::level::critical, __FILE__, __func__, __LINE__ )


// /** namespace of the organization */
// namespace tenacitas {

//     /** namespace of the project */
//     namespace log {

//         // =========================== Typedefs ===========================

//         // ============================ Pre-Declarations ==================

//         // ============================ Attributes ========================
//         /** */

//         // ============================ Functions =========================
        
//         // ============================ Classes ===========================

//         /** Base class that provides logging facilities

//         @tparam t_media offers an abstraction about the media where the
//         log messages will be printed, and it must implement:

//         <tt>typedef ptr<tt>, which defines a pointer to
//         a @p t_media object

//         <tt>template <typename t_data> void write ( const t_data & )<tt>,
//         which writes any type of data to the media

//         <tt>void new_line( level p_level, const char * p_file,
//         const char * p_function, int64_t p_line )</tt>, which starts a new
//         log line in the media

//         <tt>static t_media::ptr create( boost::thread::id )</tt>, which
//         creates a pointer to a @p t_media object

//         You can see an example of @p t_media implementation in
//         tenacitas::log::file

//         <b>A word to the programmer user</b>
//         You, dear programmer, should avoid using the
//         tenacitas::log::logger::new_line and <tt> operator<< <tt> members, and
//         use the macros commands @p log_trace, @p log_debug, @p log_info,
//         @p log_warn, @p log_error or @p log_critical instead.

//         You should use the static method tenacitas::log::logger::set_level_all
//         to define the tenacitas::log::level to be used by all the @p logger
//         objects.

//         You can change the tenacitas::log::level for all the @p logger objects
//         to be created (not the ones already created), by calling
//         tenacitas::log::logger::set_level_all again.

//         When creating a @p logger, you can define a tenacitas::log::level to be
//         used by only for it.

//         You would better call tenacitas::log::logger::can_log before actualy
//         logging a message to improve performance.

//         @nosubgrouping
//         */
//         template <typename t_media>
//         class logger {

//         public:

//             //    logger - Public Constructors

//             /** Nothing to be done in the destructor */
//             ~logger( ) {
//                 std::cout << "------------ ~logger in -----------" << std::endl;
//                 std::cout << "------------ ~logger out -----------" << std::endl;
//             }

//             /** Constructor

//             @param [in] p_level is the tenacitas::log::level to be used
//             by this @p logger object. Its default value is the log level
//             defined for all the @p logger objects, defined using the
//             @p set_level_all below, and retrieved via @p get_level_all

//             @note Please, read the documentation for tenacitas::log::level
//             to understand how log levels work in tenacitas::log

//              @attention A @p logger object can not be passed to a function that
//              will run in another thread. This is due to the concept that the
//              tenacitas::log classes work to create log for a specific thread. So,
//              if a @p logger object logs messages in a thread different from the
//              one it was created, it will mix log messages of both threads.
//             */
//             logger( level p_level = logger::get_level_all( ) )
//                 : // saves the thread id for the current thread
//                   m_thread_id( boost::this_thread::get_id( ) ),
//                   // retrieves (or creates) a @p media object
//                   m_media( m_threads_loggers.get_media( m_thread_id ) ),
//                   // saves the log level defined
//                   m_level( p_level ) { }
            

//             /** Copies are not-allowed */
//             logger ( const logger & p_logger ) = delete;

//             /** Moves are not-allowed */
//             logger ( logger && p_logger ) = delete;

//             //    logger - Public Accessors

//             /** Sets log level for all the @p logger objects

//             It only affects logger objects created after this method is called

//             @param [in] p_level is the tenacitas::log::level to be used
//             by all @p logger objects created after this method is called */
//             static void set_level_all( const level & p_level_all ) {
//                 m_level_all = p_level_all;
//             }

//             /** @return the log level for all the @p logger objects */
//             static level get_level_all( ) {
//                 return m_level_all;
//             }
            

//             /** Resets the log level defined to this @p logger object

//             @param [in] p_level is the new tenacitas::log::level to be used
//             by this @p logger object. */
//             void set_level( const level & p_level ) {
//                 m_level = p_level;
//             }

//             /** @return the log level for this @p logger object */
//             const level & get_level( ) const {
//                 return m_level;
//             }

//             /** Informs if a message log can be logged

//             Before logging anything, one should, for performance sake, verify
//             if the message will actualy be logged.
//             <b>"Why?"</b>, you may ask
//             Well, suppose a log such as:
//             <tt> log_debug( logger ) << "very long string..."
//                                      << a_double
//                                      << "another long string";
//             </tt>
//             If the log level for the @p logger object is
//             tenacitas::log::level::error, nothing will be logged. So, the work
//             done to parse the parts of the log message will have been for
//             nothing! Therefore, one should write:
//             <tt>
//             if ( logger.can_log(tenacitas::log::level::debug ) {
//                 log_debug( logger ) << "very long string..."
//                                     << a_double
//                                     << "another long string";
//             }

//             @param [in] p_level is the level one wants to check if a log
//             will be logged

//             @attention <b>tenacitas::log::level::error and
//             tenacitas::log::level::critical will allways be logged, no matter
//             the current log level. So, it is not necessary to check for the
//             log level if one wants to use @p log_error or @p log_critical</b>
//             */
//             bool can_log( const level & p_level ) const {
//                 if ( ( p_level == level::error ) ||
//                      ( p_level == level::critical ) ) {
//                     return true;
//                 }
//                 return ( p_level >= m_level );
//             }

//             //    logger - Public Processors

//             /** Starts a new log line

//              @attention: One should not call  this method directly, but use the
//              @p log_trace, @p log_debug, @p log_info, @p log_warn,
//              @p logic_error or @p log_critical

//              This methods inform the @p t_media class that a new line is
//              starting. Depending on the concrete type of @p t_media, this can
//              mean a new line in a file, a new record in a database table, or
//              anything else.

//              @param [in] p_level is the tenacitas::log::level of the new line
//              @param [in] p_file is the source file where the log is occurring
//              @param [in] p_function is the function (method) that is log is
//              occurring
//              @param [in] p_line is the line it the @p p_file where the log is
//              occurring

//              @attention A @p logger object can not be passed to a function that
//              will run in another thread. This is due to the concept that the
//              tenacitas::log classes work to create log for a specific thread. So,
//              if a @p logger object logs messages in a thread different from the
//              one it was created, it will mix log messages for both threads.

//              @throw std::string if this @p logger is being used in a
//              different thread from where it was created
//             */
//             logger & new_line( level p_level, const char * p_file,
//                                const char * p_function, int64_t p_line ) {

//                 // check if it is still in the same thread as it was created
//                 if( m_thread_id != boost::this_thread::get_id( ) ) {

//                     // NO!

//                     // to format the exception message
//                     std::stringstream l_stream;

//                     // the original thread
//                     int64_t l_this_thread_id = th2int( m_thread_id );

//                     // this 'other' thread
//                     int64_t l_new_thread_id =
//                             th2int( boost::this_thread::get_id( ) );
                    
//                     l_stream << "This log is to be used with thread "
//                              << std::dec << l_this_thread_id
//                              << ", but its being used with thread "
//                              << std::dec << l_new_thread_id ;
//                     tenacitas::log::exception l_ex(
//                         make_log_exception( l_stream.str( ) ) );
//                     throw ( l_ex );
//                 }
                
//                 // the current thread is the same as the one where this logger
//                 // object was created

//                 // but, the requested log level is allowed now?
//                 if ( can_log( p_level ) ) {
//                     // ok! so we ask the @p t_media object to actually
//                     // create a new log line
//                     m_media->new_line( p_level, p_file, p_function, p_line );
//                 }
//                 return *this;
//             }

//             /** Outputs any kind of data

//             @tparam t_data is the type of the data to be logged

//             @param p_data is the data to be logged
//             */
//             template <typename t_data>
//             logger & operator << ( const t_data & p_data ) {
//                 m_media->write( p_data );
//                 return *this;
//             }

//             /** Outputs any kind of data

//             @tparam t_data is the type of the data to be logged

//             @param p_data is the data to be logged
//             */
//             template <typename t_data>
//             logger & operator << ( t_data && p_data ) {
//                 m_media->write( p_data );
//                 return *this;
//             }

//         private:

//             //    logger - Private Internal Types

//             /** Function object to be called when a thread ends */
//             struct at_thread_end {

//                 //    at_thread_end - Public Operators

//                 /** */
//                 void operator( )( ) {
//                     std::cout << "logger:at_thread_end" << std::endl;
                    
//                     m_threads_loggers.delete_logger( boost::this_thread::get_id( ) );
//                 }
//             };

//             /** Type for the @p t_media parameter */
//             typedef t_media media;

//             /** Type for the pointer to a @p t_media object */
//             typedef typename media::ptr media_ptr;

//             /** Type for a pointer to the logger class */
//             typedef std::shared_ptr<logger> ptr;

//             /** This is collection of <tt>t_media::ptr </tt> objects and the
//                 thread to which they are associated
//             */
//             class threads_loggers {
//             public:

//                 ~threads_loggers( ) {
//                     std::cout << "~threads_loggers" << std::endl;
                    
//                     iterator l_end = m_map.end( );
//                     for ( iterator l_ite = m_map.begin( );
//                           l_ite != l_end;
//                           ++l_ite ) {

//                         std::cout << "about to lock" << std::endl;
//                         std::lock_guard<std::mutex> lock( m_mutex );
//                         std::cout << "lock released" << std::endl;

//                         l_ite->second->on_thread_end( );

//                         iterator l_aux = l_ite;
//                         ++l_aux;
                            
//                         m_map.erase( l_ite );

//                         l_ite = l_aux;
//                     }
//                 }

//                 /** Retrieves a @p media_ptr associated to a thread

//                     This method will search for a @p media_ptr in the collection of
//                     @p media_ptr, and it will return it; or it will create a
//                     @p media_ptr, associate it to the thread, and insert it into
//                     the collection

//                     @param [in] p_thread_id is the thread one wants to retrieve the
//                     @p media_ptr associated

//                 */
//                 media_ptr get_media( boost::thread::id p_thread_id ) {

//                     // already created?
//                     iterator l_ite = m_map.find( p_thread_id );

//                     if ( l_ite != m_map.end( ) ) {
//                         // yes!
//                         return ( l_ite->second );
//                     }

//                     // inserting the logger into the collection of loggers,
//                     // associating to theirs threads

//                     media_ptr l_media = media::create( p_thread_id );

//                     std::pair<boost::thread::id, media_ptr> l_pair( p_thread_id,
//                                                                     l_media );

//                     std::lock_guard<std::mutex> lock(m_mutex);

//                     // I can lock only here, because the thread l_th_id can not be
//                     // modified in another thread
//                     boost::this_thread::at_thread_exit( at_thread_end( ) );

//                     // returning a reference to the just inserted logger
//                     return (  m_map.insert( l_pair ).first->second );
//                 }

//                 /** Deletes a <tt>(media_ptr, boost::thread::id) from the collection

//                     @param [in] p_thread_id is the thread to be removed, together to
//                     its associated @p media_ptr from the collection
//                 */
//                 void delete_logger( boost::thread::id p_thread_id ) {

//                     iterator l_ite = m_map.find( p_thread_id );

//                     if ( l_ite != m_map.end( ) ) {

//                         {
//                             std::cout << "delete_logger for "
//                                       << th2int( p_thread_id ) << std::endl;
//                             // I can lock only here, because exclusion of p_thread_id can
//                             // not happen in another thread
//                             std::lock_guard<std::mutex> lock( m_mutex );

//                             l_ite->second->on_thread_end( );

//                             m_map.erase( l_ite );
//                         }
//                     }
//                 }

//             private:
//                 typedef std::map<boost::thread::id, media_ptr> map;

//                 /** The type of iterator of the collection above */
//                 typedef typename map::iterator iterator;

//                 map m_map;
                
//             };
            
//             //    logger - Private Attributes

//             /** The collection of boost::thread::id and the @p media_ptr
//             associated to it */
//             static threads_loggers m_threads_loggers;

//             /** To protect access to @p m_threads_loggers */
//             static std::mutex m_mutex;

//             /** Log level commom to all the @p logger objects */
//             static level m_level_all;
            

//             /** Thread where this @p logger was created */
//             boost::thread::id m_thread_id;

//             /** Class that actually logs the message */
//             media_ptr m_media;

//             /** Log level of this @p logger object */
//             level m_level;
//         };
//     }
// }

// using namespace tenacitas::log;

// /** Default construction for the collection */
// template <typename t_media>
// typename logger<t_media>::threads_loggers
// logger<t_media>::
// m_threads_loggers;

// /** Default construction for the mutex */
// template <typename t_media>
// std::mutex
// logger<t_media>::
// m_mutex;

// /** Default log level for all the @p logger objects is
//  tenacitas::log::level::info */
// template <typename t_media>
// level
// logger<t_media>::
// m_level_all( level::info );


// #endif


